﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Management;
using System.IO;
using System.Threading;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows;

namespace VKontakteClient
{
    /// <summary>
    /// Класс реализующий API для работы с настройками.
    /// </summary>
    class SettingsAPI
    {
        /// <summary>
        /// Реальное WCF-соединение
        /// </summary>
        private static G33KApi.VKClient _realwcf;

        /// <summary>
        /// WCF-соединение
        /// </summary>
        private static G33KApi.VKClient _wcf
        {
            get
            {
                if (_realwcf == null)
                {
                    var b = new System.ServiceModel.BasicHttpBinding()
                    {
                        CloseTimeout = new TimeSpan(0, 0, 10),
                        OpenTimeout = new TimeSpan(0, 0, 10),
                        ReceiveTimeout = new TimeSpan(0, 0, 10),
                        SendTimeout = new TimeSpan(0, 0, 10),
                        AllowCookies = false,
                        BypassProxyOnLocal = false,
                        HostNameComparisonMode = System.ServiceModel.HostNameComparisonMode.StrongWildcard,
                        MessageEncoding = System.ServiceModel.WSMessageEncoding.Text,
                        TextEncoding = System.Text.Encoding.UTF8,
                        TransferMode = System.ServiceModel.TransferMode.Buffered,
                        UseDefaultWebProxy = true,
                        ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                        {
                            MaxDepth = 32,
                            MaxStringContentLength = 8192,
                            MaxArrayLength = 16384,
                            MaxBytesPerRead = 4096,
                            MaxNameTableCharCount = 16384
                        }
                    };
                    var q = new System.ServiceModel.EndpointAddress("http://g33k.ru/VK.svc");
                    _realwcf = new VKontakteClient.G33KApi.VKClient(b, q);
                    _realwcf.Open();
                }
                return _realwcf;
            }
        }

        /// <summary>
        /// Получает через WCF разрешено ли этому ID пользоваться ВКлиентом
        /// </summary>
        /// <param name="ID">ID человека в ВКонтакте</param>
        public static bool IsAllowed(int ID)
        {
            try
            {
                var result = _wcf.IsAllowed(ID);
                return result;
            }
            catch { return true; } 
        }

        /// <summary>
        /// Получение последней версии клиента по WCF с оффсайта.
        /// </summary>
        public static Version LastClientVersion
        {
            get
            {
                var result = _wcf.GetLastClientVersion();
                return result;
            }
        }

        /// <summary>
        /// Получение даты создания последней версии клиента
        /// </summary>
        public static DateTime LastClientDatetime
        {
            get
            {
                return _wcf.GetLastClientDateTime();
            }
        }

        /// <summary>
        /// Получает размер файла клиента
        /// </summary>
        public static long LastClientFilesize
        {
            get
            {
                return _wcf.GetLastClientSize();
            }
        }

        /// <summary>
        /// Приватный криптопровайдер алгоритма Rijndael
        /// </summary>
        private static Rijndael _cryptoprovider;

        /// <summary>
        /// Приватный ключ для шифрования
        /// </summary>
        private static byte[] _key;

        /// <summary>
        /// Приватный шифратор
        /// </summary>
        private static ICryptoTransform _encryptor;

        /// <summary>
        /// Приватный дешифратор
        /// </summary>
        private static ICryptoTransform _decryptor;

        /// <summary>
        /// Приватное свойство - ключ шифрования, получаемый по серийнику жесткого диска. Реализует ленивую инициализацию.
        /// </summary>
        private static byte[] Key
        {
            get
            {
                if (_key != null) return _key;
                string Drive = "";
                foreach (ManagementObject v in new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia").Get())
                {
                    if (v["SerialNumber"] == null) continue;
                    Drive = v["SerialNumber"].ToString().Trim();
                    break;
                }
                _key = MD5.Create().ComputeHash(System.Text.Encoding.ASCII.GetBytes(Drive));
                Array.Resize(ref _key, CryptoProvider.KeySize / 8);
                return Key;
            }
        }

        /// <summary>
        /// Свойство-криптопровайдер алгоритма Rijndael. Реализует ленивую инициализацию.
        /// </summary>
        public static Rijndael CryptoProvider
        {
            get
            {
                if (_cryptoprovider != null) return _cryptoprovider;
                _cryptoprovider = Rijndael.Create();
                _cryptoprovider.IV = System.Text.Encoding.ASCII.GetBytes("InitVectSectVect");
                _cryptoprovider.Padding = PaddingMode.Zeros;
                return _cryptoprovider;
            }
        }

        /// <summary>
        /// Свойство-шифратор. Реализует ленивую инициализацию.
        /// </summary>
        public static ICryptoTransform Encryptor
        {
            get
            {
                if (_encryptor == null) _encryptor = CryptoProvider.CreateEncryptor(Key, _cryptoprovider.IV);
                return _encryptor;
            }
        }

        /// <summary>
        /// Свойство-дешифратор. Реализует ленивую инициализацию.
        /// </summary>
        public static ICryptoTransform Decryptor
        {
            get
            {
                if (_decryptor == null) _decryptor = CryptoProvider.CreateDecryptor(Key, _cryptoprovider.IV);
                return _decryptor;
            }
        }

        /// <summary>
        /// Метод загрузки настроек(десереализация в отдельном потоке)
        /// </summary>
        public static void Load()
        {
            Stream str = null;
            bool move = false;
            try
            {
                if (File.Exists("settings.dat"))
                {
                    str = new FileStream("settings.dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    DeflateStream ds = new DeflateStream(str, CompressionMode.Decompress);
                    CryptoStream cs = new CryptoStream(ds, SettingsAPI.Decryptor, CryptoStreamMode.Read);
                    var X = new BinaryFormatter().Deserialize(cs);
                    cs.Close();
                    var b = X as Settings;
                    if (b != null) App.Settings = b;
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Не удалось загрузить настройки. Возможно настройки не совместимы с этой версией приложения, или настройки перенесены с другого компьютера. В целях безопастности настройки(в том числе и пароль) могут работать только на том компьютере, где они были созданы.");
                move = true;
            }
            if (str != null)
                str.Close();
            if (move)
            {
                try
                {
                    var NewFileName = "s" + DateTime.Now.Ticks + ".dat";
                    File.Move("settings.dat", NewFileName);
                    MessageBox.Show("Старые настройки сохранены как " + NewFileName + ", при закрытии программы будут сохранены новые настройки");
                }
                catch (Exception e)
                {
                    DebugLog.DebugException(e);
                }
            }

        }
        /// <summary>
        /// Метод сохранения настроек(сериализация в файл)
        /// </summary>
        public static void Save()
        {
            Stream str = null;
            try
            {
                str = new FileStream("settings.dat", FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                DeflateStream ds = new DeflateStream(str, CompressionMode.Compress);
                CryptoStream cs = new CryptoStream(ds, SettingsAPI.Encryptor, CryptoStreamMode.Write);
                new BinaryFormatter().Serialize(cs, App.Settings);
                cs.FlushFinalBlock();
                cs.Flush();
                cs.Close();
            }
            catch
#if DEBUG
                (Exception e)
#endif
            {
                if (str != null) str.Close();
                MessageBox.Show("Не удалось сохранить настройки.");
#if DEBUG
                MessageBox.Show(e.Message);
#endif
            }
        }
    }
}
